import webapp2
from Model import *
import datetime
from google.appengine.ext import ndb
from google.appengine.api import memcache
import logging
from NotificationsUtils import *

CACHE_UPDATE_ATTEMPTS = 100

def getPosition(allStats, racer):
    logging.debug("getPosition %s", allStats)
    allStats.sort(key= lambda x: x['actualdistance'])
    i = 1
    for stat in allStats:
        if stat['racer'] == racer:
            return i
        i += 1
    logging.error("Can't find position in race for user [%s]", racer, )
    return None

def getFinalPosition1(allStats, racer,gaveUp):
    minPos = len(allStats)
    maxPos = -1
    for stat in allStats:
        if stat['FinishedPosition'] > maxPos:
            maxPos = stat['FinalPosition']
        if stat['FinishedPosition'] < minPos and stat['FinishedPosition'] != -1:
            minPos = stat['FinishedPosition']
    if gaveUp == 1:
        if maxPos == -1:
            return len(allStats) - 1
        else:
            return maxPos - 1 -1
    else:
        if minPos == len(allStats):
            return 1 - 1
        else:
            return minPos + 1 - 1
    return None

def getFinishPosition(allStats, racer ,gaveUp):
    if racer['status'] == 'finished':
        for i in range(0,len(allStats)):
            if allStats[i]['FinishedPosition'] == -1:
                return i
    else:
        for i in range(0,len(allStats)):
            if allStats[len(allStats)-i-1]['FinishedPosition'] == -1:
                return len(allStats)-i-1

class LiveRaceHandler(webapp2.RequestHandler):
    def post(self):
        """ updates CurrentStats for user and return all stats of race back, no DB access"""
        # parse POST params
        stats_key = ndb.Key('CurrentStats',int(self.request.POST.get('key')))
        race_key = ndb.Key('Race', int(self.request.POST.get('raceID')))
        speed = float(self.request.POST.get('speed'))
        actualDistance = float(self.request.POST.get('actualdistance'))

        #get all other keys for race
        allStats = memcache.get(str(race_key))
        if not allStats: #get from DB
            logging.info("Start new race status for race [%s]", race_key.id())
            allStats = RaceParticipantsStatsList(race_key)
            notInMemcache = True
        else:
            notInMemcache = False
        logging.info(allStats)
        currentRacerStats = [stats for stats in allStats if stats['key'] == stats_key.id()][0]
        currentRacerStats['speed'] = speed
        currentRacerStats['actualdistance'] = actualDistance
        status = self.request.POST.get('status')
        currentRacerStats['status'] = status
        finishedPosition = 100
        if(status == 'finished' or status == 'gaveup'):
            gaveUp = int(self.request.POST.get('gaveup'))
            finishedPosition = getFinishPosition(allStats, currentRacerStats,gaveUp)
            logging.info(finishedPosition)
            currentRacerStats['FinishedPosition'] = finishedPosition
        logging.info(allStats)
        allStats.sort(cmp_race_stats)
        logging.info(allStats)
        if(status == 'run'):
            status = 'run'
        elif(status == 'finished' or status == 'gaveup'):
            finishedPosition = int(self.request.POST.get('finishedPosition'))
            finishedTime = int(self.request.POST.get('time'))
            fininshRaceForUser(currentRacerStats['racer'], actualDistance, finishedPosition+1,\
                               currentRacerStats['race'], finishedTime, False if gaveUp else True)
            race = race_key.get()
            race.LiveUsersNumber = race.LiveUsersNumber-1;
            if(race.LiveUsersNumber == 0):
                race.Status = RaceStatus.Finished
                computeAndSetAllParticipantsRaceScore(race)
            race.put()
            data = {'messageType':'RACER_FINISHED'}
            res = sendMessageToClients(messageType = 'RACER_FINISHED',registration_ids = [x.get().RegID for x in race.ReadyParticipants] ,data=data )
        currentRacerStats['status'] = status
        #cache back after update
        if notInMemcache:
            if not memcache.add(key = str(race_key), value = allStats, time = 7200):
                logging.error("Memcahe failed to add new Race Stats")
            else:
                logging.info("Memcache add new race stats [%s]", str(race_key))
        else:
            if not memcache.replace(key = str(race_key), value = allStats, time = 7200):
                logging.error("Memcahe failed to replace Race Stats")
        logging.debug("response %s", allStats)
        self.response.content_type = 'application/json'
        self.response.out.write(json.dumps(allStats))

def cmp_race_stats(stat1,stat2):
    if stat1['FinishedPosition']!= -1 or stat2['FinishedPosition']!=-1:
        if stat1['FinishedPosition']!= -1 and stat2['FinishedPosition']!=-1:
            return stat1['FinishedPosition'] - stat2['FinishedPosition']
        else:
            if stat1['FinishedPosition']!= -1:
                if stat1['status'] == 'finished':
                    return -1
                else:
                    return 1
            else:
                if stat2['status'] == 'finished':
                    return 1
                else:
                    return -1
    if stat1['actualdistance'] >= stat2['actualdistance']:
        return -1
    else:
        return 1

class oldLiveRaceHandler(webapp2.RequestHandler):
    def get(self):
        race_key = ndb.Key('Race', int(self.request.GET['race']))
        self.response.out.write(json.dumps(RaceParticipantsStatsList(race_key)))
   
    def post(self):
        stats_key = ndb.Key('CurrentStats',int(self.request.POST.get('key')))
        stats = stats_key.get()  # urlsafe = self.request.POST['key']))
        stats.Speed = float(self.request.POST.get('speed'))
        stats.ActualDistance = float(self.request.POST.get('actualdistance'))
        if(self.request.POST.get('status') == 'run'):
            stats.Status = RacerStatus.Run
        elif(self.request.POST.get('status') == 'finished'):
            stats.Status = RacerStatus.Finished
            stats.FinishedTime = datetime.datetime.now()
            race_key = 	ndb.Key('Race',int(self.request.POST.get('raceID')))
            race = race_key.get()
            race.LiveUsersNumber = race.LiveUsersNumber-1;

            if(race.LiveUsersNumber == 0):
                race.Status = RaceStatus.Finished
                computeAndSetAllParticipantsRaceScore() #need to be done!!!
		
            race.put()
            data = {'messageType':'RACER_FINISHED'}
            res = sendMessageToClients(messageType = 'RACER_FINISHED',registration_ids = [x.get().RegID for x in race.ReadyParticipants] ,data=data )
        stats.put()

    def postNew(self):
        race_key = ndb.Key('Race', int(self.request.GET['race']))
        stats_key = ndb.Key('CurrentStats',int(self.request.POST.get('key')))

        client = memcache.Client()
        race_dict = client.gets(str(race_key.id()))
        if(race_key is None):
            logging.info("Race not found in memcache",race_key.id())
            #load fro datastore?
        raceStats = race_dict['RaceStats']
        userStats = raceStats[str(stats_key.id())]
        userStats['Speed'] = float(self.request.POST.get('speed'))
        userStats['ActualDistance'] = float(self.request.POST.get('actualdistance'))
        race_dict['RaceStats'][str(stats_key.id())] = userStats
        if(self.request.POST.get('finished')!=None):
            userStats['FinishedTime'] = datetime.datetime.now()
        if(writeToCache(str(race_key.id()),race_dict,client) is None):
            logging.error("Could not update cache",str(race_key.id()))

        self.response.out.write(json.dumps(race_dict))

def writeToCache(key,value,client):
    i=0
    while i<CACHE_UPDATE_ATTEMPTS: # Retry loop
        if client.cas(key, value):
            return True
    return False

def RaceParticipantsStatsList(race):
    allParticipants = CurrentStats.query(CurrentStats.Race == race)
    return [{ 'key': participant.key.id(), 'race': participant.Race.id(), 'racer': participant.Racer.id(), 'speed': participant.Speed, 'name': participant.Name,
             'actualdistance': participant.ActualDistance, 'FinishedPosition':participant.FinishedPosition, 'status': ("finished" if participant.Status==RacerStatus.Finished else "run")} for participant in allParticipants]

def computeAndSetAllParticipantsRaceScore(race):
    pass


    #TODO in a smart way for all the participants in once

def fininshRaceForUser(userId, actualDistance, position, raceId, finishedTime, finished):
    logging.debug("User %s has finished, position=%s, distance=%s", userId, position, actualDistance)
    user = ndb.Key('User', userId).get()
    if not user.TotalDistance:
        user.TotalDistance = 0
    user.TotalDistance += actualDistance
    user.LastRun = datetime.date.today()
    if not user.BestPlace or user.BestPlace > position:
        user.BestPlace = position
    user.put()

    race = ndb.Key('Race', raceId).get()

    #save history
    m, s = divmod(finishedTime, 60)
    h, m = divmod(m, 60)
    raceHistory = RaceHistory(
        Racer = ndb.Key('User', userId),
        Grade = -1,
        ActualDistance = actualDistance,
        FinishedTime = datetime.time(h, m, s),
        IsFinished = finished,
        Position = position,
        Distance = race.Distance
    )

    raceHistory.put()